//******************************************************************************
//this code is protected by the GNU affero GPLv3
//author:Sylvain BERTRAND <sylvain.bertrand AT gmail dot com>
//                        <digital.ragnarok AT gmail dot com>
//******************************************************************************
#include "globals.h"

static void obj_release(struct client *c,s32 surface_slot)
{
    c->objs[surface_slot].state=CLIENT_OBJ_INVALID;
    if(c->objs[surface_slot].surface.pending_buffer_slot<CLIENT_OBJS_MAX)
      client_obj_unref(c,c->objs[surface_slot].surface.pending_buffer_slot);
    if(c->objs[surface_slot].surface.pending_opaque_region_slot<CLIENT_OBJS_MAX)
      client_obj_unref(c,
                      c->objs[surface_slot].surface.pending_opaque_region_slot);
    if(c->objs[surface_slot].surface.pending_input_region_slot<CLIENT_OBJS_MAX)
      client_obj_unref(c,
                       c->objs[surface_slot].surface.pending_input_region_slot);
}

s8 surface_destroy(struct client *c,s32 surface_slot)
{
  s32 n=client_obj_unref(c,surface_slot);
  if(!n) c->objs[surface_slot].state&=~CLIENT_OBJ_VISIBLE;
  return wl_display_ev_delete_id(c,c->objs[surface_slot].id);
}

s8 surface_new_visible(struct client *c,u32 surface_id)
{
  s8 r=LWL_OK;
  s32 free_slot=CLIENT_FIRST_VALID_ID;
  while(1){
    if(free_slot==CLIENT_OBJS_MAX){
      r=LWL_ERR;
      break;
    }
    if(c->objs[free_slot].state&CLIENT_OBJ_INVALID){
      c->objs[free_slot].state=CLIENT_OBJ_SURFACE|CLIENT_OBJ_VISIBLE;
      c->objs[free_slot].release=obj_release;
      c->objs[free_slot].id=surface_id;
      c->objs[free_slot].n=1;//the wire has a reference on it

      c->objs[free_slot].surface.pending_buffer_slot=CLIENT_OBJS_MAX;
      c->objs[free_slot].surface.pending_damages_n=0;
      c->objs[free_slot].surface.frame_callback_valid=0;
      c->objs[free_slot].surface.pending_opaque_region_slot=CLIENT_OBJS_MAX;
      c->objs[free_slot].surface.pending_input_region_slot=CLIENT_OBJS_MAX;
      break;
    }
    ++free_slot;
  }
  return r;
}

s8 surface_attach(struct client *c,s32 surface_slot,u32 buffer_id,s32 x,s32 y)
{
  s8 r=LWL_OK;
  s32 buffer_slot=CLIENT_FIRST_VALID_ID;

  while(1){ 
    if(buffer_slot==CLIENT_OBJS_MAX){
      PERR("client(%d):surface::attach bad buffer_id=%u\n",c->so,buffer_id);
      r=LWL_ERR;goto exit;
    }
    if(!(c->objs[buffer_slot].state&CLIENT_OBJ_INVALID)
                                  &&c->objs[buffer_slot].state&CLIENT_OBJ_BUFFER
                                           &&c->objs[buffer_slot].id==buffer_id)
      break;
    ++buffer_slot;
  }

  client_obj_ref(c,buffer_slot);
  c->objs[surface_slot].surface.pending_buffer_slot=buffer_slot;
  c->objs[surface_slot].surface.pending_buffer_x=x;
  c->objs[surface_slot].surface.pending_buffer_y=y;

exit:
  return r;
}

s8 surface_damage(struct client *c,s32 surface_slot,s32 x,s32 y,s32 w,s32 h)
{
  s8 r=LWL_OK;
  s32 n=c->objs[surface_slot].surface.pending_damages_n;
  if(n==PENDING_DAMAGES_N_MAX){
    PERR("client(%d):surface::damage no more room for damage\n",c->so);
    r=LWL_ERR;goto exit;
  }

  struct damage *d=&c->objs[surface_slot].surface.pending_damages[n];
  d->x=x;
  d->y=y;
  d->h=h;
  d->w=w;
  ++(c->objs[surface_slot].surface.pending_damages_n);

exit:
  return r;
}

s8 surface_frame(struct client *c,s32 surface_slot,u32 callback_id)
{
  c->objs[surface_slot].surface.frame_callback_valid=1;
  c->objs[surface_slot].surface.frame_callback_id=callback_id;
  return LWL_OK;
}

s8 surface_set_opaque_region(struct client *c,s32 surface_slot,u32 region_id)
{
  s8 r=LWL_OK;
  s32 region_slot=CLIENT_FIRST_VALID_ID;

  while(1){
    if(region_slot==CLIENT_OBJS_MAX){
      PERR("client(%d):surface::set_opaque_region bad region_id=%u\n",c->so,
                                                                     region_id);
      r=LWL_ERR;goto exit;
    }
    if(!(c->objs[region_slot].state&CLIENT_OBJ_INVALID)
                                  &&c->objs[region_slot].state&CLIENT_OBJ_REGION
                                           &&c->objs[region_slot].id==region_id)
      break;
    ++region_slot;
  }

  client_obj_ref(c,region_slot);
  c->objs[surface_slot].surface.pending_opaque_region_slot=region_slot;

exit:
  return r;
}

s8 surface_set_input_region(struct client *c,s32 surface_slot,u32 region_id)
{
  s8 r=LWL_OK;
  s32 region_slot=CLIENT_FIRST_VALID_ID;

  while(1){
    if(region_slot==CLIENT_OBJS_MAX){
      PERR("client(%d):surface::set_input_region bad region_id=%u\n",c->so,
                                                                     region_id);
      r=LWL_ERR;goto exit;
    }
    if(!(c->objs[region_slot].state&CLIENT_OBJ_INVALID)
                                  &&c->objs[region_slot].state&CLIENT_OBJ_REGION
                                           &&c->objs[region_slot].id==region_id)
      break;
    ++region_slot;
  }

  client_obj_ref(c,region_slot);
  c->objs[surface_slot].surface.pending_input_region_slot=region_slot;

exit:
  return r;
}

s8 surface_commit(struct client *c,s32 surface_slot)
{
  (void)c;(void)surface_slot;
  //TODO:rendering with the driver
  //TODO:switch pending and current
  return 0;
}
